#include<tool/libcell_tools_algorithm.h>

#include<Mesh/libcell_iterator.h>
#define quote lib_cell_quote


void Mesh_adjust_mesh_to_unit_hexahedron(Mesh* mesh)
{
    //auto vit=mesh->vertices.begin();
    //double xs[2]={vit->second->point[0],vit->second->point[0]},ys[2]={vit->second->point[1],vit->second->point[1]},zs[2]={vit->second->point[2],vit->second->piont[2]};
    double centriod[3]={0};
    template_v* v=NULL;
    for(auto vit=mesh->v_begin(mesh);vit!=mesh->v_end(mesh);vit++)
    {
        v=(template_v*)(quote(vit));
       
        centriod[0]+=v->point[0];centriod[1]+=v->point[1];centriod[2]+=v->point[2];

    }

   centriod[0]/=(double)mesh->num_v(mesh);
   centriod[1]/=(double)mesh->num_v(mesh);
   centriod[2]/=(double)mesh->num_v(mesh);
  
    double max=0; 
    for(auto vit=mesh->v_begin(mesh);vit!=mesh->v_end(mesh);vit++)
    {
        v=(template_v*)(quote(vit));
        v->point[0]=v->point[0]-centriod[0];
        v->point[1]=v->point[1]-centriod[1];
        v->point[2]=v->point[2]-centriod[2];

        if(fabs(v->point[0])>max)
        {
            max=fabs(v->point[0]);
        }
        if(fabs(v->point[1])>max)
        {
            max=fabs(v->point[1]);
        }
        if(fabs(v->point[2])>max)
        {
            max=fabs(v->point[2]);
        } 
    }
     for(auto vit=mesh->v_begin(mesh);vit!=mesh->v_end(mesh);vit++)
     {
        v=(template_v*)(quote(vit));

        v->point[0]/=max;
        v->point[1]/=max;
        v->point[2]/=max;

     }

}

Node* Mesh_adjust_halffaces(Mesh* m,template_c* c)
{
     Node* hfs=node_copy(c->halffaces);
     Node* re=NULL;
     re=node_pushback(re,hfs->value);
     hfs=node_remove(hfs,hfs);
     int flag=1;
     while(flag)
     {
        template_hf* hf=(template_hf*)(re->value);
        if(hf->vertices_size<=1)
        {
            free_node(hfs);
            return re;
        }
        template_v* v=hf->vertices[1];
        flag=0;
        for(Node* nit=hfs;nit!=NULL;nit=(Node*)(nit->Next))
        {
          
            template_hf* hf1=(template_hf*)(nit->value);
            if(m->vertex_is_in_face(m,v,hf1->face))
            {
                flag=1;
                re=node_pushback(re,nit->value);
                hfs=node_remove(hfs,nit);
                break;
            }
        }
     }
     free_node(hfs);
     return node_reverse(re);
     
}
//modified 
static Node* my_get_cut_vertices_node(Mesh*m,Node* faces)
{
    //printf("begin generate node2\n");
    Node* node1=NULL,*node2=NULL;
    for(Node* nit=faces;nit!=NULL;nit=(Node*)(nit->Next))
    {
        template_f* f=(template_f*)(nit->value);
        for(int i=0;i<2;i++)
        {
          //  printf("v id%d\n",f->vertices[i]->id ); 
            if(node_find(node2,f->vertices[i])==NULL)
            {
                if(node_find(node1,f->vertices[i])==NULL)
                {
               //     printf("one\n");
                    node1=node_overlying(node1,f->vertices[i]);
                }
                else
                {
               //     printf("two\n" );
                    node1=node_delete_value(node1,f->vertices[i]);
                    node2=node_overlying(node2,f->vertices[i]);
                }
            }
            
        }
    }
    Node* node=node_copy(node1);
  //  printf("node 1 size:%d node 2 size:%d\n",node_size(node1),node_size(node2) );
    for(Node* nit=node;nit!=NULL;nit=(Node*)(nit->Next))
    {
        template_v*v=(template_v*)(nit->value);
        if(m->vertex_is_boundary(m,*v))
        {
   //         printf("boudnary\n");
            node2=node_overlying(node2,v);
            //node1=node_delete_value(node1,v);
        }
    }
    free_node(node);
    free_node(node1);
    return node2;
}
static inline void my_cut_oneside(Mesh* m,Node* node2,Int_RB_Tree* tree)
{
    Int_RB_Tree* tr1=(Int_RB_Tree*)malloc(sizeof(Int_RB_Tree));
    int_rb_tree_init(tr1);
    Int_RB_Tree* tr2=(Int_RB_Tree*)malloc(sizeof(Int_RB_Tree));
    int_rb_tree_init(tr2);
    for(Node* nit=node2;nit!=NULL;nit=(Node*)(nit->Next))
    {
        template_v* v=(template_v*)(nit->value);
        template_v* v1=m->create_vertexv(m,v->point,3);
        tr1->insert(tr1,v1->id,v);
        tr2->insert(tr2,v->id,v1); 
    }
    Node*node_c=NULL;
    for(auto it=tree->begin(tree);it.it!=NULL;it++)
    {
        template_c* c=(template_c*)(*it);
        Node* hfs_n=NULL;
        for(auto it1=m->chf_begin(m,*c);it1!=m->chf_end(m,*c);it1++)
        {
            Node* hf_n=NULL; 
            for(int i=0;i<2;i++)
            {
                template_v* v=quote(it1)->vertices[i];
                template_v* v1=(template_v*)(tr2->find(tr2,v->id));
                if(v1!=NULL)
                {
                    hf_n=node_pushback(hf_n,v1);
                }
                else
                {
                    hf_n=node_pushback(hf_n,v); 
                }
            }
            hfs_n=node_overlying(hfs_n,node_reverse(hf_n));           
        }

        node_c=node_overlying(node_c,hfs_n); 

        m->delete_cell(m,*c,true);
    }

    for(Node*nit=node_c;nit!=NULL;nit=(Node*)(nit->Next))
    {
        Node* n1=(Node*)(nit->value);
        int size1=node_size(n1);
        template_hf**temp_hf=(template_hf**)malloc(sizeof(template_hf*)*size1);
        template_v**temp_v=NULL;
        int j=0;
        for(Node* nit1=n1;nit1!=NULL;nit1=(Node*)(nit1->Next))
        {
            Node* n2=(Node*)(nit1->value);
            int size=node_size(n2);
            temp_v=(template_v**)malloc(sizeof(template_v*)*size);
            int i=0;
            for(Node* nit2=n2;nit2!=NULL;nit2=(Node*)(nit2->Next))
            {
                temp_v[i]=(template_v*)(nit2->value);
                i++;
            }
            template_f* f=m->create_facev(m,temp_v,size);
            temp_hf[j]=m->create_halfface(m,f,temp_v,size);
            free(temp_v);
            j++;
        }
        m->create_cellf(m,temp_hf,size1);
        free(temp_hf);
    }  


    for(Node* nit=node_c;nit!=NULL;nit=(Node*)(nit->Next))
    {
        Node* n1=(Node*)(nit->value);
        for(Node* nit1=n1;nit1!=NULL;nit1=(Node*)(nit1->Next))
        {
            Node* n2=(Node*)(nit1->value);
            free_node(n2);
        }

        free_node(n1);
    }
    free_node(node_c);

    int_rb_tree_free(tr1);
    int_rb_tree_free(tr2);
}


static Int_RB_Tree* my_get_connected_cells(Mesh* m,Int_RB_Tree* tree,Node* faces)
{
    Int_RB_Tree*re=(Int_RB_Tree*)malloc(sizeof(Int_RB_Tree));
    int_rb_tree_init(re);
    auto it1=tree->begin(tree);
    template_c* c=(template_c*)(*it1);
    if(c==NULL)
    {
        return re;
    }  
    Node* tempn=NULL;
    tempn=node_overlying(tempn,c);
    tree->erase(tree,c->id);
    re->insert(re,c->id,c);
    while(tempn!=NULL)
    {
        Node* tempn1=NULL;
        for(Node* nit=tempn;nit!=NULL;nit=(Node*)(nit->Next))
        {
            template_c* c2=(template_c*)(nit->value);
            for(auto it=m->chf_begin(m,*c2);it!=m->chf_end(m,*c2);it++)
            {
                if(node_find(faces,quote(it)->face)!=NULL)
                {
                    continue;
                }
                template_c* c3=m->s_opposite_halfface(quote(it))->cell;
                if(c3==NULL||tree->find(tree,c3->id)==NULL)
                {
                    continue;
                }
                tempn1=node_overlying(tempn1,c3);
             
                tree->erase(tree,c3->id);
                re->insert(re,c3->id,c3);
            }            
        }
        free_node(tempn);
        tempn=tempn1;        
    } 


    return re;
}
Mesh* Mesh_cut_along_the_curvef(Mesh* m,Node* faces)
{
    m->manifold_require=1;
   // printf("face size:%d\n",node_size(faces) );
    Node* node2=my_get_cut_vertices_node(m,faces);
    //printf("node2 size:%d\n",node_size(node2) ); 
    Int_RB_Tree* tree=(Int_RB_Tree*)malloc(sizeof(Int_RB_Tree));
    int_rb_tree_init(tree); 
    for(Node* nit=node2;nit!=NULL;nit=(Node*)(nit->Next))
    {
        template_v* v=(template_v*)(nit->value);
        for(Node*nit1=v->cells;nit1!=NULL;nit1=(Node*)(nit1->Next))
        {
            template_c* c=(template_c*)(nit1->value);
            tree->insert(tree,c->id,c);  
        } 
    }
    
    //printf("tree size:%d\n",tree->size );

    //printf("tree1 size:%d\n",tree1->size );
   // my_cut_oneside(m,node2,tree);
   // my_cut_oneside(m,node2,tree1);
    while(tree->size>0)
    {

        Int_RB_Tree* tree1=my_get_connected_cells(m,tree,faces);

        my_cut_oneside(m,node2,tree1);
        int_rb_tree_free(tree1);
    }




    free_node(node2);
    int_rb_tree_free(tree);
    //int_rb_tree_free(tree1);



    return m;
}
Mesh* Mesh_cut_along_the_curvev(Mesh* m,Node* vertices)
{   
    Node* edges=NULL;
    template_v* vs[2];
    for(Node* nit=vertices;nit!=NULL;nit=(Node*)(nit->Next))
    {
        template_v* v=(template_v*)(nit->value);
        Node* nit1=(Node*)(nit->Next);
        template_v* v1=(template_v*)(vertices->value);
        if(nit1!=NULL)
        {
            v1=(template_v*)(nit1->value);
        }
        vs[0]=v;vs[1]=v1;
        template_f* f=m->get_facev(m,vs,2);
        if(f==NULL)
        {

           // printf("cuowu\n");
            continue;
        }
        edges=node_overlying(edges,f);
        

    } 
    //printf("node :%d\n",node_size(edges) );
    Mesh_cut_along_the_curvef(m,edges);

    free_node(edges);

    return m;
}